﻿using System;
using System.Collections.Generic;
using Sinacor.Infra.Service.Common;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;
using Sinacor.Infra.Service.Security.Authorization;
using Sinacor.Infra.Service.Validation;
using Sinacor.Servico.Bovespa.Movimento.BusinessEntity;
using Sinacor.Servico.Bovespa.Movimento.BusinessLogic;

namespace Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessLogic
{
    /// <summary>
    /// Fgualberto.7Comm - 11079
    /// </summary>
    public class PreBoleto : BaseBusinessLogic
    {
        #region Variaveis
        private ResourceAccess.PreBoleto _preBoleto;
        private BusinessEntity.Ordem _ordemMae = null;
        private BusinessEntity.Ordem _ordemPreBoleto = null;
        #endregion

        #region Construtor
        public PreBoleto()
            : base()
        {
            _preBoleto = new Sinacor.Servico.Bovespa.ManutencaoOrdens.ResourceAccess.PreBoleto();
        }

        #endregion 

        #region Constantes

        const string POP = "PP";
        const string D1SIM = "D1SIM";
        const int ERRO_ESTRATEGIA_INVALIDA = 109503;
        const int ERRO_OBJETO_NULO = 109491;
        const int ERRO_ORDEM_JA_DISTRIBUIDA = 103006;
        const int ERRO_ORDEM_NAO_CONFIG_PARA_CASAMENTO = 109508;
        const int ERRO_USUARIO_NAO_POSSUI_PERMISSAO = 109507;
        const int ERRO_PREBOLETO_NULO = 109489;
        const int ERRO_PREBOLETO_CASADO = 109506;
        const int ERRO_DESCASAR_PREBOLETO = 109505;
        const int ERRO_CONSULTAR_NULO = 109487;
        const int ERRO_VALOR_CAMBIO = 109502;
        const int ERRO_USUARIO_SEM_PREMISSAO_ACESSO = 109507;
        const int ERRO_MOVIMENTO_DIFERENTE_ABERTO = 109509;
        const int ERRO_VALOR_NAO_MULTIPLO = 109497;
        const int ERRO_AUMENTA_ORDEM_DESATIVADO = 109498;
        const int ERRO_PRECO_COMPRA_INVALIDO = 109499;
        const int ERRO_PRECO_VENDA_INVALIDO = 109500;

        #endregion 

        #region Funções

        /// <summary>
        /// Disponibiliza a alteração do Tipo de Casamento, Estratégia e Câmbio de um
        /// determinado pré-boleto
        /// </summary>
        /// <param name="entity"></param>
        public void AlterarPreBoleto(BusinessEntity.PreBoleto entity)
        {
            BusinessEntity.Parameter.FiltroPreBoletoChave filtroPreBoleto =
                    new Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Parameter.FiltroPreBoletoChave();

            if (entity == null)
            {
                throw new BusinessException(ERRO_PREBOLETO_NULO);
            }

            this.VerificarPermissaoAcesso();
            this.ValidarOrdem(entity.Ordem);

            if (_ordemMae.Distribuida.HasValue && _ordemMae.Distribuida.Value)
                throw new BusinessException(ERRO_ORDEM_JA_DISTRIBUIDA);            

            filtroPreBoleto.Numero = entity.Numero;
            BusinessEntity.PreBoleto preBoleto = this.ObterPreBoleto(filtroPreBoleto);

            if (entity.QuantidadeCasada > 0 && (entity.QuantidadeCasada == entity.Quantidade))
            {
                throw new BusinessException(ERRO_PREBOLETO_CASADO);
            }

            this.ValidarCambio(entity.ValorCambio);
            this.ValidarEstrategia(preBoleto, _ordemMae);
            _preBoleto.AlterarPreBoleto(entity);
        }

        /// <summary>
        /// Disponibiliza a alteração do Tipo de Casamento, Estratégia e Câmbio de uma
        /// diversos pré-boleto
        /// </summary>
        /// <param name="entityList">Lista com BusinessEntity.PreBoleto</param>
        public void AlterarPreBoletos(List<BusinessEntity.PreBoleto> entityList)
        {
            foreach (BusinessEntity.PreBoleto item in entityList)
            {
                this.AlterarPreBoleto(item);
            }
        }

        /// <summary>
        /// Consulta os pré-boletos de uma determinada ordem, para uma determinada data de
        /// pregão
        /// </summary>
        /// <param name="parameter"></param>
        public List<BusinessEntity.PreBoleto> ConsultarPreBoletos(BusinessEntity.Parameter.FiltroPreBoleto parameter)
        {
            List<BusinessEntity.PreBoleto> preBoletos;

            if (parameter == null)
            {
                throw new BusinessException(ERRO_CONSULTAR_NULO);
            }
            else
            {
                preBoletos = _preBoleto.ConsultarPreBoletos(parameter);
            }

            return preBoletos;
        }

        /// <summary>
        /// Realiza o descasamento de um pré-boleto
        /// </summary>
        /// <param name="parameter"></param>
        private void DescasarPreBoleto(BusinessEntity.Parameter.FiltroPreBoletoChave parameter)
        {
            BusinessEntity.Resultado resultado;

            resultado = _preBoleto.DescasarPreBoleto(parameter);
            if (!resultado.Sucesso)
            {
                if (resultado.CodigoErro == -1)
                {
                    throw new BusinessException(ERRO_DESCASAR_PREBOLETO);
                }
                else
                {
                    this.TratarExcecoes(resultado);
                }
            }
        }

        /// <summary>
        /// Exclui um determinado pré-boleto
        /// </summary>
        /// <param name="parameter"></param>
        public void ExcluirPreBoleto(BusinessEntity.Parameter.FiltroPreBoletoChave parameter)
        {
            BusinessEntity.Parameter.FiltroPreBoletoChave filtroPreBoleto =
                new Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Parameter.FiltroPreBoletoChave();

            if (parameter == null)
                throw new BusinessException(ERRO_PREBOLETO_NULO);

            this.VerificarPermissaoAcesso();
            filtroPreBoleto.Numero = parameter.Numero;
            BusinessEntity.PreBoleto preBoleto = this.ObterPreBoleto(filtroPreBoleto);

            this.ValidarOrdem(preBoleto.Ordem);

            if (_ordemMae.Distribuida.HasValue && _ordemMae.Distribuida.Value)
                throw new BusinessException(ERRO_ORDEM_JA_DISTRIBUIDA);

            this.DescasarPreBoleto(parameter);
            _preBoleto.ExcluirPreBoleto(parameter);
        }

        /// <summary>
        /// Exclui diversos pré-boletos
        /// </summary>
        /// <param name="entityList"></param>
        public void ExcluirPreBoletos(List<BusinessEntity.Parameter.FiltroPreBoletoChave> entityList)
        {
            foreach (BusinessEntity.Parameter.FiltroPreBoletoChave item in entityList)
            {
                this.ExcluirPreBoleto(item);
            }
        }

        /// <summary>
        /// Gerar número para o Pré-Boleto
        /// </summary>
        private int GerarNumero()
        {
            return _preBoleto.GerarNumero();
        }

        /// <summary>
        /// Inclui um pré-boleto para uma determinada ordem do sistema
        /// </summary>
        /// <param name="entity"></param>
        public int IncluirPreBoleto(BusinessEntity.PreBoleto entity)
        {
            int numeroPreBoleto = 0;

            if (entity == null)
                throw new BusinessException(ERRO_PREBOLETO_NULO);

            this.VerificarPermissaoAcesso();
            this.ValidarOrdem(entity.Ordem);

            // Validação RuleSet
            SinacorValidation validation = new SinacorValidation();
            validation.Validate<BusinessEntity.PreBoleto>(entity, "GrupoValidacaoPreBoleto");
            validation = null;

            this.ValidarQuantidade(entity);
            this.ValidarPreco(_ordemMae, entity);
            this.ValidarCambio(entity.ValorCambio);
            this.ValidarEstrategia(entity, _ordemMae);

            numeroPreBoleto = this.GerarNumero();
            entity.Numero = numeroPreBoleto;
            _preBoleto.IncluirPreBoleto(entity, _ordemMae);

            return numeroPreBoleto;
        }

        /// <summary>
        /// Inclui uma lista de pré-boletos para diversas ordens do sistema
        /// </summary>
        /// <param name="entityList"></param>
        public void IncluirPreBoletos(List<BusinessEntity.PreBoleto> entityList)
        {
            foreach (BusinessEntity.PreBoleto item in entityList)
            {
                this.IncluirPreBoleto(item);
            }
        }

        /// <summary>
        /// Obtém um determinado pré-boleto
        /// </summary>
        /// <param name="parameter"></param>
        public BusinessEntity.PreBoleto ObterPreBoleto(BusinessEntity.Parameter.FiltroPreBoletoChave parameter)
        {

            BusinessEntity.PreBoleto preBoleto;

            if (parameter == null)
            {
                throw new BusinessException(ERRO_PREBOLETO_NULO);
            }
            else
            {
                preBoleto = _preBoleto.ObterPreBoleto(parameter);
            }

            return preBoleto;
        }

        /// <summary>
        /// Realiza o tratamento de exceções geradas pelas procedures e functions
        /// </summary>
        /// <param name="entity">BusinessEntity.Resultado</param>
        private void TratarExcecoes(BusinessEntity.Resultado entity)
        {
            if (entity.Argumentos == null || entity.Argumentos == "")
            {
                throw new BusinessException(entity.CodigoErro);
            }
            else
            {
                List<Object> _params = ErrorMessage.ConvertXmlArgumentsToParameters(entity.Argumentos);
                if (_params != null)
                    throw new BusinessException(entity.CodigoErro, _params.ToArray());
            }
        }

        /// <summary>
        /// Valida o valor do câmbio para o pré-boleto
        /// </summary>
        /// <param name="valorCambio"></param>
        private void ValidarCambio(Nullable<Decimal> valorCambio)
        {
            if (valorCambio != null && valorCambio <= 0)
            {
                throw new BusinessException(ERRO_VALOR_CAMBIO);
            }
        }

        /// <summary>
        /// Para realizar o Casamento ou o Descasamento, o Movimento de Ordens deve estar aberto.
        /// </summary>
        private void ValidarEstadoMovimento()
        {
            Sinacor.Servico.Bovespa.Movimento.BusinessEntity.InformacoesMovimento retorno = null;
            Sinacor.Servico.Bovespa.Movimento.BusinessLogic.ControleMovimentoOrdens ordens = new Sinacor.Servico.Bovespa.Movimento.BusinessLogic.ControleMovimentoOrdens();

            retorno = ordens.ObterInformacoesMovimento(null);

            if (retorno.EstadoMovimento != Sinacor.Servico.Bovespa.Movimento.BusinessEntity.Enum.TipoMovimento.Aberto)
                throw new BusinessException(ERRO_MOVIMENTO_DIFERENTE_ABERTO);
        }

        /// <summary>
        /// Valida o tipo de estratégia adotado para o pré-boleto
        /// </summary>
        /// <param name="preBoleto"></param>
        /// <param name="ordem"></param>
        private void ValidarEstrategia(BusinessEntity.PreBoleto preBoleto, BusinessEntity.Ordem ordem)
        {
            if (ordem.TipoOrdem.Value != BusinessEntity.Enum.TipoOrdem.Financiamento)
            {
                Titulo.BusinessEntity.Titulo dadosTitulo =
                        new Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Titulo();

                Titulo.BusinessLogic.Titulo titulo =
                new Sinacor.Servico.Bovespa.Titulo.BusinessLogic.Titulo();

                dadosTitulo.Codigo = ordem.Ativo;
                dadosTitulo.DataOrdem = ordem.DataOrdem;

                dadosTitulo = titulo.ObterTitulo(dadosTitulo);

                // validar se Ativo da Ordem Mãe é POP
                if (dadosTitulo.CodigoISIN.Substring(7, 2) == POP)
                {
                    if (preBoleto.Estrategia != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoEstrategia.POP)
                        throw new BusinessException(ERRO_ESTRATEGIA_INVALIDA);
                }
            }

            if (ordem.Mercado == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoCompra
                    || ordem.Mercado == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoVenda)
            {
                if (preBoleto.Estrategia != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoEstrategia.DesmancheBox
                    && preBoleto.Estrategia != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoEstrategia.Nenhuma)
                    throw new BusinessException(ERRO_ESTRATEGIA_INVALIDA);
            }
            else if (ordem.Mercado == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Vista)
            {
                if (preBoleto.Estrategia != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoEstrategia.POP
                    && preBoleto.Estrategia != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoEstrategia.Nenhuma)
                    throw new BusinessException(ERRO_ESTRATEGIA_INVALIDA);
            }
            else if (preBoleto.Estrategia != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoEstrategia.Nenhuma)
            {
                throw new BusinessException(ERRO_ESTRATEGIA_INVALIDA);
            }
        }

        /// <summary>
        /// Valida a ordem do pré-boleto
        /// </summary>
        /// <param name="entity"></param>
        private void ValidarOrdem(BusinessEntity.ChaveOrdem entity)
        {
            ManutencaoOrdens manutencaoOrdens = new ManutencaoOrdens();
            ControleMovimentoOrdens movimentoOrdens = new ControleMovimentoOrdens();
            InformacoesMovimento informacoesMovimento = null;

            _ordemMae = 
                manutencaoOrdens.ObterOrdem
                (
                    new BusinessEntity.ChaveOrdem()
                    {
                        DataOrdem = entity.DataOrdem,
                        NumeroOrdem = entity.NumeroOrdem,
                        NumeroOrdemSubSequencia = 0
                    }
                );

            _ordemPreBoleto = manutencaoOrdens.ObterOrdem(entity);

            if (_ordemMae == null || _ordemPreBoleto == null)
            {
                throw new BusinessException(ERRO_OBJETO_NULO);
            }

            if (_ordemMae.IndicadorCasaOrdem.Value == false)
            {
                throw new BusinessException(ERRO_ORDEM_NAO_CONFIG_PARA_CASAMENTO);
            }

             //recuperar Data de Movimento
            informacoesMovimento = movimentoOrdens.ObterInformacoesMovimento(new Movimento.BusinessEntity.DataMovimento());

            if (informacoesMovimento.DataMovimento > _ordemMae.DataValidade)
            {
                if (!Sinacor.Servico.Bovespa.Acesso.BusinessLogic.Acesso.VerificarAcessoFuncao(D1SIM))
                {
                    throw new BusinessException(ERRO_USUARIO_NAO_POSSUI_PERMISSAO);
                }
            }            
        }

        /// <summary>
        /// Valida o preço do pré-boleto. Utilizado apenas quando ordem é do tipo Limitada
        /// </summary>
        /// <param name="ordem">BusinessEntity.Ordem</param>
        /// <param name="preBoleto">BusinessEntity.PreBoleto</param>
        private void ValidarPreco(BusinessEntity.Ordem ordem, BusinessEntity.PreBoleto preBoleto)
        {
            BusinessLogic.Ordem blOrdem = new Ordem();
            decimal precoLimite = 0;

            if (ordem.TipoOrdem == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoOrdem.Limitada)
            {
                precoLimite = blOrdem.ObterPrecoLimiteOrdem(ordem);
                if (ordem.NaturezaOperacao == BusinessEntity.Enum.NaturezaOperacao.Compra)
                {
                    if (preBoleto.Preco > precoLimite)
                    {
                        throw new BusinessException(ERRO_PRECO_COMPRA_INVALIDO);
                    }
                }
                else
                {
                    if (preBoleto.Preco < precoLimite)
                    {
                        throw new BusinessException(ERRO_PRECO_VENDA_INVALIDO);
                    }
                }
            }
        }

        /// <summary>
        /// Valida a quantidade do pré-boleto.
        /// </summary>
        /// <param name="entity">BusinessEntity.ChaveOrdem</param>
        private void ValidarQuantidade(BusinessEntity.PreBoleto entity)
        {
            decimal resultadoLoteMinimo = 0;
            int resto = 0;
            decimal resultado = 0;
            decimal saldoOrdem = 0;
            decimal quantidade = 0;

            Titulo.BusinessLogic.Titulo titulo =
                    new Sinacor.Servico.Bovespa.Titulo.BusinessLogic.Titulo();

            Titulo.BusinessEntity.Parameter.ConsultarLoteMinimoNegociacao loteMinimo =
                    new Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Parameter.ConsultarLoteMinimoNegociacao();

            BusinessLogic.Ordem blOrdem = new Ordem() ;
            Parametro.BusinessLogic.Parametro blParametro = new Sinacor.Servico.Bovespa.Parametro.BusinessLogic.Parametro();
            Parametro.BusinessEntity.Parametro beParametro;

            loteMinimo._DataPregao = _ordemMae.DataOrdem;
            loteMinimo._Titulo = _ordemMae.Ativo;

            resultadoLoteMinimo = titulo.ConsultarLoteMinimoNegociacao(loteMinimo);

            if (resultadoLoteMinimo != 0)
            {
                resto = Convert.ToInt32(entity.Quantidade.Value % resultadoLoteMinimo);
                resultado = Convert.ToDecimal(entity.Quantidade.Value / resultadoLoteMinimo);

                if (resto != 0 || resultado == 0)
                {
                    throw new BusinessException(ERRO_VALOR_NAO_MULTIPLO);
                }
            }

            saldoOrdem = blOrdem.ObterSaldoOrdem(new BusinessEntity.ChaveOrdem()
            {
                NumeroOrdem = entity.Ordem.NumeroOrdem,
                NumeroOrdemSubSequencia = entity.Ordem.NumeroOrdemSubSequencia,
                DataOrdem = entity.Ordem.DataOrdem
            }, entity.DataPregao);

            if (entity.Quantidade.Value > (_ordemPreBoleto.Quantidade - saldoOrdem))
            {
                beParametro = blParametro.ObterParametro();
                if (beParametro.PreBoletoAumentaOrdem)
                {
                    quantidade = entity.Quantidade.Value + _ordemPreBoleto.Quantidade.Value;
                    _preBoleto.AlterarQuantidadeOrdem(entity.Ordem, quantidade);
                }
                else
                {
                    throw new BusinessException(ERRO_AUMENTA_ORDEM_DESATIVADO);
                }
            }
           
        }

        /// <summary>
        /// Verifica a permissão de acesso para manipular os pré-boletos
        /// </summary>
        private void VerificarPermissaoAcesso()
        {
            if (!AuthorizationContext.Current.CheckUserAccess("IORD", "ORD") && !AuthorizationContext.Current.CheckUserAccess("AORD", "ORD") && !AuthorizationContext.Current.CheckUserAccess("EORD", "ORD"))
                throw new BusinessException(ERRO_USUARIO_SEM_PREMISSAO_ACESSO);
        }

        #endregion 
    }
}